﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;

namespace SimpleORM
{
    public class DynamicClassAttribute
    {
        #region Properties

        public Type Type { get; set; }
        public List<string> ConstructorParameters { get; private set; }
        public Dictionary<string, string> PropertyParameters { get; private set; }

        #endregion

        #region Fields

        private const string ATTRIBUTENAME = "#ATTRIBUTENAME#";
        private const string ATTRIBUTEPARAMETERS = "#ATTRIBUTEPARAMETERS#";

        #endregion

        #region Ctor

        public DynamicClassAttribute()
        {
            ConstructorParameters = new List<string>();
            PropertyParameters = new Dictionary<string, string>();
        }

        #endregion

        #region Methods

        public override string ToString()
        {
            //string txt =
            //    Properties.Resources.AttributeTemplate.Replace(ATTRIBUTENAME, Type.FullName).
            //                                           Replace(ATTRIBUTEPARAMETERS, OnGettingParameters());

            //return txt;
            return null;
        }

        protected virtual string OnGettingParameters()
        {
            StringBuilder b = new StringBuilder();

            if (ConstructorParameters.Count > 0)
                b.Append(ConstructorParameters[0]);
            for (int i = 1; i < ConstructorParameters.Count; i++)
                b.AppendFormat(", {0}", ConstructorParameters[i]);

            foreach (var pp in PropertyParameters)
                b.AppendFormat(b.Length > 0 ? ", {0} = {1}" : "{0} = {1}", pp.Key, pp.Value);

            return b.ToString();
        }

        #endregion
    }
    public class DynamicClassProperty
    {
        #region Properties

        public string Name { get; set; }
        public Type Type { get; set; }
        public bool Nullable { get; set; }
        public List<DynamicClassAttribute> Attributes { get; private set; }

        #endregion

        #region Fields

        private const string ATTRIBUTES = "#ATTRIBUTES#";
        private const string PROPERTYTYPE = "#PROPERTYTYPE#";
        private const string PROPERTYNAME = "#PROPERTYNAME#";

        #endregion

        #region Ctor

        public DynamicClassProperty()
        {
            Attributes = new List<DynamicClassAttribute>();
        }

        #endregion

        #region Methods

        public override string ToString()
        {
            StringBuilder b = new StringBuilder();
            foreach (var attr in Attributes)
                b.Append(attr.ToString());

            //string property = Nullable && Type.IsValueType ?
            //    Properties.Resources.PropertyTemplate.Replace(ATTRIBUTES, b.ToString()).
            //                                          Replace(PROPERTYTYPE, string.Format("System.Nullable<{0}>", Type.FullName)).
            //                                          Replace(PROPERTYNAME, Name)
            //    :
            //    Properties.Resources.PropertyTemplate.Replace(ATTRIBUTES, b.ToString()).
            //                                          Replace(PROPERTYTYPE, Type.FullName).
            //                                          Replace(PROPERTYNAME, Name);

            //return property;

            return null;
        }

        #endregion
    }
    public class DynamicClassBuilder
    {
        #region Properties

        public string FullName { get { return string.Format("{0}.{1}", Namespace, Name); } }
        public string Name { get; set; }
        public string Namespace { get; set; }
        public Type Parent { get; set; }
        public List<DynamicClassProperty> Properties { get; private set; }
        public List<DynamicClassAttribute> Attributes { get; private set; }

        #endregion

        #region Fields

        private const string ATTRIBUTES = "#ATTRIBUTES#";
        private const string CLASSNAME = "#CLASSNAME#";
        private const string PARENTNAME = "#PARENTNAME#";
        private const string PROPERTIES = "#PROPERTIES#";
        private const string USINGS = "#USINGS#";
        private const string NAMESPACE = "#NAMESPACE#";

        private static Dictionary<string, Type> dicTypes = new Dictionary<string, Type>();

        #endregion

        #region Ctor

        public DynamicClassBuilder()
        {
            Properties = new List<DynamicClassProperty>();
            Attributes = new List<DynamicClassAttribute>();
            Parent = typeof(CDObject);
        }

        #endregion

        #region Methods

        #region Methods - Static

        public static object CreateInstance(KeyValueList<string, object> values, KeyValueList<string, Type> nullValueTypes = null)
        {
            var builder = new DynamicClassBuilder();
            foreach (var pair in values)
            {
                DynamicClassProperty item = new DynamicClassProperty
                {
                    Name = pair.Key,
                    Type = (pair.Value != null) ? pair.Value.GetType() : ((
                        nullValueTypes == null) ? typeof(object) : (
                        nullValueTypes.ContainsKey(pair.Key) ?
                        nullValueTypes[pair.Key] : typeof(object)))
                };
                builder.Properties.Add(item);
            }

            object source = Activator.CreateInstance(builder.Create());

            foreach (var pair in values)
                source.SetPropertyValue(pair.Key, pair.Value);

            return source;
        }

        #endregion

        #region Methods - Public

        public Type Create()
        {
            return Create(GenerateClassString());
        }

        private Type Create(string cls)
        {
            lock (dicTypes)
            {
                if (!dicTypes.ContainsKey(cls))
                {
                    var paras = new CompilerParameters(
                        AppDomain.CurrentDomain.GetAssemblies().
                        Select(c => !c.IsDynamic ? c.Location : null).ToArray());

                    paras.GenerateExecutable = false;
                    paras.GenerateInMemory = true;

                    CompilerResults result =
                        new CSharpCodeProvider().CompileAssemblyFromSource(paras, cls);

                    dicTypes[cls] = result.CompiledAssembly.GetType(FullName);
                }

                return dicTypes[cls];
            }
        }

        #endregion

        #region Methods - Private

        private string GenerateClassString()
        {
            StringBuilder b = new StringBuilder();
            foreach (var dp in Properties)
                b.Append(dp.ToString());

            StringBuilder bA = new StringBuilder();
            foreach (var attr in Attributes)
                bA.Append(attr.ToString());

            if (string.IsNullOrWhiteSpace(Name))
                Name = "DynamicModelDefault";
            if (string.IsNullOrWhiteSpace(Namespace))
                Namespace = "YuLinTu.DynamicModels";

            string tmpClass =
                "".Replace(CLASSNAME, Name).
                                                           Replace(PROPERTIES, b.ToString()).
                                                           Replace(ATTRIBUTES, bA.ToString()).
                                                           Replace(PARENTNAME, Parent.FullName).
                                                           Replace(NAMESPACE, Namespace);

            return tmpClass;
        }

        #endregion

        #endregion
    }
}
